#include "stdafx.h"

#include "config.hpp"
#include <iostream>
#include <fstream>
#include <iterator>

#include <boost/program_options/options_description.hpp>
#include <boost/program_options.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>

namespace po = boost::program_options; 
using namespace std;

const char* config::DEFAULT_CIPHER_FILE = "generic_cipher_filename.pem";
const char* config::DEFAULT_KEY_FILE = "generic_key_filename.pem"; 
const char* config::DEFAULT_IV_FILE = "generic_iv_filename.pem";
const char* config::DEFAULT_SEED_FILE = "generic_seed_filename.saved";
const char* config::DEFAULT_REGEX = "(\\d+)-(\\d+)";
const char* config::DEFAULT_VOLUME_MAP_FILE = "\0";
const char* config::DEFAULT_EXPORT_FILENAME = "BCK";

//const char* config::DEFAULT_INGDIRECT_PINPAD_COORDS_REGEX = "\\s+";
//const char config::DEFAULT_INGDIRECT_PINPAD_COORDS_SEP = '§';

// A helper function to simplify the main part.
template<class T>
ostream& operator<<(ostream& os, const vector<T>& v)
{
    copy(v.begin(), v.end(), ostream_iterator<T>(cout, " ")); 
    return os;
}

std::ostream& operator<<(std::ostream& ost, const config& c)
{
	ost << "allow first char as punctuation = " << c.first_char_as_punct << "\n";
	//ost << "use ingdirect pinpad coordinates = " << c.use_ingdirect_pinpad_coords << "\n";
	ost << "min distribution value = " << c.min_distr_value << "\n";
	ost << "max distribution value = " << c.max_distr_value << "\n";
	ost << "max inner token length = " << c.max_inner_token_len << "\n";
	ost << "max token length = " << c.max_token_len << "\n";
	ost << "inner token min distr value = " << c.inner_token_min_distr_value << "\n";
	ost << "inner token max distr value = " << c.inner_token_max_distr_value << "\n";
	ost << "allowed punctuation = " << c._allowed_punctuation << "\n";
	ost << "forbidden punctuation = " << c._forbidden_punctuation << "\n";
	ost << "cipher file = " << c._cipher_file << "\n";
	ost << "key file = " << c._key_file << "\n";
	ost << "iv file = " << c._iv_file << "\n";
	ost << "seed file = " << c._seed_file << "\n";
	ost << "subindices regex = " << c._regex << "\n";
	//ost << "ingdirect pinpad coordinates regex = " << c._ingdirect_pinpad_coords_regex << "\n";
	ost << "config filename = " << c._config_filename << "\n";
ost << "export filename = " << c._export_filename << "\n";
	ost << "ingdirect configuration file" << c._ingdirect_conffile << "\n";
	//ost << "ingdirect pinpad coordinates = " << c.ingdirect_pinpad_coords << "\n";
	//ost << "ingdirect pinpad coordinates separator = " << c.ingdirect_pinpad_coords_sep << "\n";
   ost << "volume mapping filename" << c._volume_map_file << "\n";

	return ost;
}

const char* config::CONFIG_OPTS[config::CONFIG_TOT_OPTS][2] = 
{
	{"allow-first-char-as-punct", "1 = allow first character as punctuation"},
	{"min-distribution-value", "lower uniform distribution value for token generation"},
	{"max-distribution-value", "upper uniform distribution value for token generation"},
	{"max-token-len", "maximum token length"},
	{"cipher-file", "/path_to_cipher/filename"},
	{"key-file", "/path_to_key/filename"},
	{"iv-file", "/path_to_iv/filename"},
	{"seed-file", "/path_to_seed_status/filename"},
	{"allowed-punctuation", "allowed punctuation set"},
	{"forbidden-punctuation", "forbidden punctuation set"},
   {"volume-map-file", "/path_to_volume_map/filename"},
{"export-file", "/path_to/export_file"}
	//{"use-ingdirect-pinpad-coords", "use ingdirect pinpad coordinates"}
};

const char* config::HIDDEN_OPTS[config::HIDDEN_TOT_OPTS][2] =
{
	{"max-inner-token-len","maximum inner token length"},
	{"inner-token-min-distr-value", "lower uniform distribution value for inner token"},
	{"inner-token-max-distr-value","upper uniform distribution value for inner token"},
	{"regex","regular expression definition"},
	{"ingdirect-file", "ingdirect configuration file"}
	//{"ingdirect-pinpad-coords","ingdirect pinpad coordinates"},
	//{"ingdirect-pinpad-coords-separator","ingdirect separator between pinpad coordinates"},
	//{"ingdirect-pinpad-coords-regex","ingdirect regex for pinpad coordinates"}
};

config::config(const char* _filename)
{
	init(_filename);
};


void config::init(const char* _filename)
{	
	_config_filename = _filename;

	try {   

		if(_config_filename.empty())
		{
			throw ("config file has to be specified: -c /path_to_config/filename"); 
		}

        // Declare a group of options that will be 
        // allowed both on command line and in
        // config file
#ifdef _WIN32
        po::options_description config("Configuration");
#else
	po::options_description config("Configuration", po::options_description::m_default_line_length*2);
#endif
        config.add_options()	
		(CONFIG_OPTS[config::FIRST_AS_PUNCT][0], po::value<bool>(&first_char_as_punct)->default_value(true), CONFIG_OPTS[config::FIRST_AS_PUNCT][1])
		(CONFIG_OPTS[config::MIN_DISTR_VALUE][0], po::value<unsigned short>(&min_distr_value)->default_value(0x21), CONFIG_OPTS[config::MIN_DISTR_VALUE][1])    
		(CONFIG_OPTS[config::MAX_DISTR_VALUE][0], po::value<unsigned short>(&max_distr_value)->default_value(0x7E), CONFIG_OPTS[config::MAX_DISTR_VALUE][1])
		(CONFIG_OPTS[config::MAX_TOKEN_LEN][0],  po::value<unsigned short>(&max_token_len)->default_value(8u),  CONFIG_OPTS[config::MAX_TOKEN_LEN][1])
		(CONFIG_OPTS[config::CIPHER_FILE][0],po::value<std::string>(&_cipher_file)->default_value(config::DEFAULT_CIPHER_FILE), CONFIG_OPTS[config::CIPHER_FILE][1])
		(CONFIG_OPTS[config::KEY_FILE][0],  po::value<std::string>(&_key_file)->default_value(config::DEFAULT_KEY_FILE),  CONFIG_OPTS[config::KEY_FILE][1])
		(CONFIG_OPTS[config::IV_FILE][0], po::value<std::string >(&_iv_file)->default_value(config::DEFAULT_IV_FILE),CONFIG_OPTS[config::IV_FILE][1])
		(CONFIG_OPTS[config::SEED_FILE][0], po::value<std::string>(&_seed_file)->default_value(config::DEFAULT_SEED_FILE), CONFIG_OPTS[config::SEED_FILE][1])	
		(CONFIG_OPTS[config::ALLOW_PUNCT][0], po::value<std::string>(&_allowed_punctuation)->default_value(""), CONFIG_OPTS[config::ALLOW_PUNCT][1])
		(CONFIG_OPTS[config::FORBID_PUNCT][0], po::value<std::string>(&_forbidden_punctuation)->default_value(""), CONFIG_OPTS[config::FORBID_PUNCT][1])
      (CONFIG_OPTS[config::VOLUME_MAP_FILE][0], po::value<std::string >(&_volume_map_file)->default_value(config::DEFAULT_VOLUME_MAP_FILE),CONFIG_OPTS[config::VOLUME_MAP_FILE][1])
		//(CONFIG_OPTS[config::USE_INGDIRECT_PINPAD_COORDS][0], po::value<bool>(&use_ingdirect_pinpad_coords)->default_value(false), CONFIG_OPTS[config::USE_INGDIRECT_PINPAD_COORDS][1])
(CONFIG_OPTS[config::EXPORT_FILENAME][0], po::value<std::string>(&_export_filename)->default_value(config::DEFAULT_EXPORT_FILENAME), CONFIG_OPTS[config::EXPORT_FILENAME][1])
      ;

        // Hidden options, will be allowed both on command line and
        // in config file, but will not be shown to the user.
        po::options_description hidden("Hidden options");
        hidden.add_options()            
		(HIDDEN_OPTS[config::MAX_INNER_TOKEN_LEN][0], po::value<unsigned short>(&max_inner_token_len)->default_value(100u), HIDDEN_OPTS[config::MAX_INNER_TOKEN_LEN][1])
		(HIDDEN_OPTS[config::INNER_TOKEN_MIN_DISTR_VALUE][0], po::value<unsigned short>(&inner_token_min_distr_value)->default_value(0x00), HIDDEN_OPTS[config::INNER_TOKEN_MIN_DISTR_VALUE][1])
		(HIDDEN_OPTS[config::INNER_TOKEN_MAX_DISTR_VALUE][0], po::value<unsigned short>(&inner_token_max_distr_value)->default_value(0x7F), HIDDEN_OPTS[config::INNER_TOKEN_MAX_DISTR_VALUE][1])
		(HIDDEN_OPTS[config::REGEX][0], po::value<std::string>(&_regex)->default_value(config::DEFAULT_REGEX), HIDDEN_OPTS[config::REGEX][1])
		(HIDDEN_OPTS[config::INGDIRECT_CONFIG_FILE][0], po::value<std::string>(&_ingdirect_conffile)->default_value(""), HIDDEN_OPTS[config::INGDIRECT_CONFIG_FILE][1])
		//(HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS][0], po::value<std::string>(), HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS][1])
		//(HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS_SEP][0], po::value<char>(&ingdirect_pinpad_coords_sep)->default_value(config::DEFAULT_INGDIRECT_PINPAD_COORDS_SEP), HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS_SEP][1])
		//(HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS_REGEX][0], po::value<std::string>(&_ingdirect_pinpad_coords_regex)->default_value(config::DEFAULT_INGDIRECT_PINPAD_COORDS_REGEX), HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS_REGEX][1])
        ;

        po::options_description config_file_options;
        config_file_options.add(config).add(hidden);
      
        po::variables_map vm;

	ifstream ifs(_filename);
	if(ifs.bad()) throw ("config file not found: " + _config_filename);


    	store(parse_config_file(ifs, config_file_options), vm);
    	notify(vm);           	
	
	if(!vm[CONFIG_OPTS[config::FIRST_AS_PUNCT][0]].empty())
	{
		first_char_as_punct = vm[CONFIG_OPTS[config::FIRST_AS_PUNCT][0]].as< bool >();
	}

	if(!vm[CONFIG_OPTS[config::MIN_DISTR_VALUE][0]].empty())
	{
		min_distr_value = vm[CONFIG_OPTS[config::MIN_DISTR_VALUE][0]].as< unsigned short >();
	}

	if(!vm[CONFIG_OPTS[config::MAX_DISTR_VALUE][0]].empty())
	{
		max_distr_value = vm[CONFIG_OPTS[config::MAX_DISTR_VALUE][0]].as< unsigned short >();
	}

	if(!vm[CONFIG_OPTS[config::MAX_TOKEN_LEN][0]].empty())
	{
		max_token_len = vm[CONFIG_OPTS[config::MAX_TOKEN_LEN][0]].as< unsigned short >();
	}

	if(!vm[CONFIG_OPTS[config::CIPHER_FILE][0]].empty())
	{	
		_cipher_file = vm[CONFIG_OPTS[config::CIPHER_FILE][0]].as< std::string>();
	}

	if(!vm[CONFIG_OPTS[config::KEY_FILE][0]].empty())
	{
		_key_file = vm[CONFIG_OPTS[config::KEY_FILE][0]].as< std::string>();
	}

	if(!vm[CONFIG_OPTS[config::IV_FILE][0]].empty())
	{
		_iv_file = vm[CONFIG_OPTS[config::IV_FILE][0]].as< std::string>();
	}
	
	if(!vm[CONFIG_OPTS[config::SEED_FILE][0]].empty())
	{
		_seed_file = vm[CONFIG_OPTS[config::SEED_FILE][0]].as<std::string>();
	}

	if(!vm[CONFIG_OPTS[config::ALLOW_PUNCT][0]].empty())
	{
		_allowed_punctuation = vm[CONFIG_OPTS[config::ALLOW_PUNCT][0]].as< std::string >();
	}

	if(!vm[CONFIG_OPTS[config::FORBID_PUNCT][0]].empty())
	{
		_forbidden_punctuation = vm[CONFIG_OPTS[config::FORBID_PUNCT][0]].as< std::string >();
	}

  if(!vm[CONFIG_OPTS[config::VOLUME_MAP_FILE][0]].empty())
	{
		_volume_map_file = vm[CONFIG_OPTS[config::VOLUME_MAP_FILE][0]].as<std::string>();
	}
   
  if(!vm[CONFIG_OPTS[config::EXPORT_FILENAME][0]].empty())
	{
		_export_filename = vm[CONFIG_OPTS[config::EXPORT_FILENAME][0]].as < std::string >();
	}

	/*if(!vm[CONFIG_OPTS[config::USE_INGDIRECT_PINPAD_COORDS][0]].empty())
	{
		use_ingdirect_pinpad_coords = vm[CONFIG_OPTS[config::USE_INGDIRECT_PINPAD_COORDS][0]].as < bool >();
	}*/

	if(!vm[HIDDEN_OPTS[config::INGDIRECT_CONFIG_FILE][0]].empty())
	{
		_ingdirect_conffile = vm[HIDDEN_OPTS[config::INGDIRECT_CONFIG_FILE][0]].as< std::string>();
	}

	if(!vm[HIDDEN_OPTS[config::MAX_INNER_TOKEN_LEN][0]].empty())
	{
		max_inner_token_len = vm[HIDDEN_OPTS[config::MAX_INNER_TOKEN_LEN][0]].as< unsigned short >();
	}

	if(!vm[HIDDEN_OPTS[config::INNER_TOKEN_MIN_DISTR_VALUE][0]].empty())
	{
		inner_token_min_distr_value = vm[HIDDEN_OPTS[config::INNER_TOKEN_MIN_DISTR_VALUE][0]].as< unsigned short >();	
	}

	if(!vm[HIDDEN_OPTS[config::INNER_TOKEN_MAX_DISTR_VALUE][0]].empty())
	{
		inner_token_max_distr_value = vm[HIDDEN_OPTS[config::INNER_TOKEN_MAX_DISTR_VALUE][0]].as< unsigned short >();
	}

	if(!vm[HIDDEN_OPTS[config::REGEX][0]].empty())
	{
		_regex = vm[HIDDEN_OPTS[config::REGEX][0]].as< std::string >();
	}

	/*if(!vm[HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS][0]].empty())
        {
                std::string input(vm[HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS][0]].as< std::string>());
                boost::regex expression(vm[HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS_REGEX][0]].as< std::string>() );
                       
		boost::regex_split(std::back_inserter(ingdirect_pinpad_coords), input, expression); 
				
        }

	if(!vm[HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS_REGEX][0]].empty())
	{
		_ingdirect_pinpad_coords_regex	= vm[HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS_REGEX][0]].as< std::string >();
	}

	if(!vm[HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS_SEP][0]].empty())
	{
	  	ingdirect_pinpad_coords_sep = vm[HIDDEN_OPTS[config::INGDIRECT_PINPAD_COORDS_SEP][0]].as< char >();
	}*/
	

    }
    catch(exception& e)
    {
		cout << e.what() << std::endl;
    }       

}

bool config::get_first_char_as_punct()
{
	return first_char_as_punct;
}


std::string const& config::get_allowed_punctuation() const
{
	return _allowed_punctuation;
}

std::string const& config::get_forbidden_punctuation() const
{
	return _forbidden_punctuation;
}

#include "volume_master.hpp"

std::string const& config::get_cipher_file()
{
  _cipher_file = volume_master::get_pathname(_cipher_file, _volume_map_file);
	return _cipher_file; 
}

std::string const& config::get_key_file()
{
  _key_file = volume_master::get_pathname(_key_file, _volume_map_file);
	return _key_file;
}

std::string const& config::get_iv_file()
{
  _iv_file = volume_master::get_pathname(_iv_file, _volume_map_file);
	return _iv_file;
}

std::string const& config::get_seed_file()
{
  _seed_file = volume_master::get_pathname(_seed_file, _volume_map_file);
	return _seed_file;
}


std::string const& config::get_regex() const
{
	return _regex;
}

std::string const& config::get_volume_map_filename() const
{
	return _volume_map_file;
}

config& config::operator=(config const& _cfg)
{
	first_char_as_punct = _cfg.first_char_as_punct;
	//use_ingdirect_pinpad_coords = _cfg.use_ingdirect_pinpad_coords;

	min_distr_value = _cfg.min_distr_value;
	max_distr_value = _cfg.max_distr_value;
	max_inner_token_len = _cfg.max_inner_token_len;
	max_token_len = _cfg.max_token_len;
	inner_token_min_distr_value = _cfg.inner_token_max_distr_value;
	inner_token_max_distr_value = _cfg.inner_token_max_distr_value;

	_allowed_punctuation = _cfg._allowed_punctuation;
	_forbidden_punctuation = _cfg._forbidden_punctuation;

	_cipher_file = _cfg._cipher_file;
	_key_file = _cfg._key_file;
	_iv_file = _cfg._iv_file;
	_seed_file = _cfg._seed_file;

	_regex = _cfg._regex;
	_ingdirect_conffile = _cfg._ingdirect_conffile;

	//_ingdirect_pinpad_coords_regex = _cfg._ingdirect_pinpad_coords_regex;
	//ingdirect_pinpad_coords_sep = _cfg.ingdirect_pinpad_coords_sep;	
	//ingdirect_pinpad_coords = _cfg.ingdirect_pinpad_coords;
   
   _volume_map_file = _cfg._volume_map_file;	
	_config_filename = _cfg._config_filename;
	_export_filename = _cfg._export_filename;

	
	return *this;
}

unsigned short& config::get_min_distr_value() const
{
	return min_distr_value;
}
unsigned short& config::get_max_distr_value() const
{
	return max_distr_value;
}

unsigned short& config::get_max_inner_token_len() const
{
	return max_inner_token_len;
}
unsigned short& config::get_max_token_len() const
{
	return max_token_len;
}
unsigned short& config::get_inner_token_min_distr_value() const
{
	return inner_token_min_distr_value;
}
unsigned short& config::get_inner_token_max_distr_value() const
{
	return inner_token_max_distr_value;
}

std::string const& config::get_keypad_config_filename() const
{
	return _ingdirect_conffile;
}

/*
const std::vector<std::string> config::get_ingdirect_pinpad_coords()
{
	return ingdirect_pinpad_coords;
}

const std::string config::get_ingdirect_pinpad_coords_regex()
{
	return _ingdirect_pinpad_coords_regex;
}

const char config::get_ingdirect_pinpad_coords_sep()
{
	return ingdirect_pinpad_coords_sep;
}

bool config::get_use_ingdirect_pinpad_coords()
{
	return use_ingdirect_pinpad_coords;
}
*/